//
//	IOBUS - HAL dotnetmf
//
//  (c) 2008 mocom software GmbH & Co KG
//
//  $Author: volker $
//  $Date: 2009-09-25 14:43:44 +0200 (Fr, 25. Sep 2009) $
//  $Revision: 500 $
//
//-------------------------------------------------------------------------------------------
//
#include <tinyhal.h>
#define DEF_NO_BASIC_TYPES	1
#include <iobus.h>

#undef DEBUG
#define DEBUG	0

#ifndef USART_FLOW_HALF_DUPLEX
#define USART_FLOW_HALF_DUPLEX   		0x8000
#endif

#define USED_COM_PORT			4


static HAL_CONTINUATION	TimerTaskContinuation;
static HAL_COMPLETION	TimerTaskCompletion;
static UINT32			iobus_char_time = 0;
static unsigned			tobesend;
static UINT8*			txdata;
static UINT32			iobus_cycle_time;
static bool				iobus_enabled = 0;
static unsigned			recv_count = 0;
static IOBusFrame		*data_available;
static UINT32			fails = 0;




					
void IOBusHalTask()
{
	static UINT64 old=0;
	UINT64 delta, tag;
	
	tag = HAL_Time_CurrentTime();
	delta = (tag - old) / 10;
	if (delta >= iobus_cycle_time) {
		old = tag;
		IOBusFrame *frame = data_available;
		data_available = NULL;
		iobus_enabled = IOBusMasterIndication( frame );
	} else {
		fails++;
	}		 
}

// schedules iobus_timer_continuation
static void iobus_timer_completion( void* arg )  
{
	// Schedule the timer task to run as a continuation
	if ( !TimerTaskContinuation.IsLinked() )
	{
		TimerTaskContinuation.Enqueue();
	}
	if ( iobus_enabled ) {
		TimerTaskCompletion.EnqueueDelta( iobus_cycle_time );		
	}	
}

// tx isr callback
static int txcallback( int com, unsigned burst, UINT8 volatile *txport, void *arg )
{
	if ( tobesend <= burst )
		burst = tobesend;

	if ( burst )
	{
		tobesend = CPU_USART_WriteBurstToTxBuffer( USED_COM_PORT, txdata, burst );
		if ( tobesend )
		{
			txdata = &txdata[burst-tobesend];
		}
	} else {
		//
	}
	return 0;
}

// isr, read a byte from UART and call IOBusRxAssemble until there is no more
static int rxcallback( int com, unsigned burst, UINT8 volatile *rxport, void *arg )
{
	while ( burst-- )
	{
		recv_count++;
		IOBusRxAssemble( *rxport, 0 );
	}
	return 0;
}

static int linecallback( int com, unsigned lsr, unsigned data, void *arg )
{
	IOBusRxAssemble( data, lsr );
	return 0;
}


// tell HW to start
void IOBusHalStartCapture( IOBusObjectDir *dir, unsigned tag )
{
	// NO HARDWARE ON MASTER
}


// send a message
void IOBusHalSendData( UINT8 *data, unsigned len )
{
	tobesend = CPU_USART_WriteBurstToTxBuffer( USED_COM_PORT, data, len );
	if ( tobesend )
	{
		txdata = &data[len-tobesend];
	}
}

// Return true if a gap excceds inter message gap
int IOBusHalGapDetected( int hunt )
{
	static UINT64 gap_timer = 0;
	UINT64 now, timeframe, delta;
	int	gap = FALSE;

	if ( hunt )
	{
		// capture gap and reset recev counter
		gap_timer = HAL_Time_CurrentTime();
		recv_count = 0;
		gap = TRUE;
	}
	else
	{
		// get current time in uSec (Time_CurrentTime delivers 100ns)
		now = HAL_Time_CurrentTime();
		// time since last call greater than gap time?
		timeframe = (( IOBUS_INTERMESSAGEGAP + IOBUS_INTERMESSAGEGAP + recv_count ) * iobus_char_time ) * 10;
		delta = now - gap_timer;
		if ( delta > timeframe ) 
		{
			gap = TRUE;
		}
	}

	return gap;
}

UINT64 IOBusHalGetTime()
{
	return HAL_Time_CurrentTime();
}

void IOBusIndicate( IOBusFrame *frame )
{
	data_available = frame;
}


void *IOBusHalMemoryCopy( void *d, void *s, unsigned l )
{
	UINT8 *c = (UINT8*)d;
	memcpy( d, s, l );
	
	return (void *) &c[l];
}

void IOBusHalMemorySet( void *d, int val, unsigned len )
{
	memset( d, val, len );
}

void IOBUS_Uninitialize()
{
	iobus_enabled = FALSE;
	TimerTaskCompletion.Abort();
	TimerTaskContinuation.Abort();	
	CPU_USART_Uninitialize( USED_COM_PORT );	
}

// called by OS
void IOBUS_Initialize()
{
	IOBusSetCycleMode(FALSE);
}

static void IOBusHalSetCycleTime()
{
	// cycle time
	iobus_cycle_time = 100000 * (115200 / IOBUS_BAUDRATE);
	debug_printf( "CYCLE TIME=%ums\r\n", iobus_cycle_time/1000 );
}

int IOBusEnable( unsigned long config )
{
	int rval = iobus_enabled;

	if ( config )
	{
		UINT64 byte_per_second = ( IOBUS_BAUDRATE / ( 1 + 8 + 1 ) );
		// char time in us
		iobus_char_time = 1000000 / byte_per_second;
		// init UART
		CPU_USART_Initialize( USED_COM_PORT, IOBUS_BAUDRATE , USART_PARITY_NONE, 8, 1, USART_FLOW_HALF_DUPLEX );
		// install uart callbacks
		CPU_USART_SetOption( USED_COM_PORT, USART_OPT_SET_TX_CALLBACK, ( UINT32 ) txcallback );
		CPU_USART_SetOption( USED_COM_PORT, USART_OPT_SET_RX_CALLBACK, ( UINT32 ) rxcallback );
		CPU_USART_SetOption( USED_COM_PORT, USART_OPT_SET_LINE_CALLBACK, ( UINT32 ) linecallback );

		debug_printf( "*** ENABLE IOBUS ***\r\n" );
		debug_printf( "CHAR TIME =%uus\r\n", iobus_char_time );
		debug_printf( "MAX NODES =%u\r\n", IOBUS_MAX_NODES );
		debug_printf( "MAX DATA  =%u\r\n\r\n", IOBUS_MAX_DATA_LENGTH );

		// calc in ms
		IOBusHalSetCycleTime();

		data_available = NULL;

		// reset master
		IOBusMasterInit( config , iobus_cycle_time / 1000);

		// timer
		TimerTaskContinuation.InitializeCallback(( HAL_CALLBACK_FPN ) IOBusHalTask, NULL );
		TimerTaskCompletion.InitializeForUserMode(( HAL_CALLBACK_FPN ) iobus_timer_completion, NULL );

		// start timer
		TimerTaskCompletion.EnqueueDelta( iobus_cycle_time );
		iobus_enabled = TRUE;
	}
	else
	{
		IOBusMasterInit( 0, 0 );
		iobus_enabled = FALSE;
	}

	return rval;
}
